package ch.sfug.sequence {
	import flash.events.Event;
	import flash.events.EventDispatcher;
	
	import ch.sfug.data.Iterator;	

	/**
	 * with this class you can queue up different ISequenceChilds and start the hole sequence.
	 * you can use it for example like this:
	 * 
	 * var seq:Sequence = new Sequence();
	 * @author mich
	 */
	public class Sequencer extends EventDispatcher {
		
		private var childs:Iterator;
		private var loops:Boolean;
		
		public function Sequencer() {
			super();
			childs = new Iterator();
			loops = false;
		}
		
		/**
		 * starts the sequence.
		 */
		public function start(  ):void {
			stop();
			childs.reset();
			processNextChild();
		}

		public function loop(  ):void {
            loops = true;
            start();
        }

        public function processNextChild( e:Event = null ):void {
        	
			// remove old listener
			var cur:ISequenceChild = ISequenceChild( childs.current() );
			if( cur != null && cur.hasToWait() ) EventDispatcher( cur ).removeEventListener( Event.COMPLETE, processNextChild );

			if ( childs.hasNext() ) {
				
				var sc:ISequenceChild = ISequenceChild( childs.next() );
				
				if( sc.hasToWait() ) EventDispatcher( sc ).addEventListener( Event.COMPLETE, processNextChild );
				sc.execute();
				if( !sc.hasToWait() ) processNextChild();
				
			} else {
				handleEnd();
			}
		}
		
		protected function handleEnd():void {
			if ( loops ) {
				reset();
				start();
			} else {
				dispatchEvent(new Event(Event.COMPLETE));
			}
		}

		
		
		
		/**
		 * will adjust properties of the obj Object.
		 * use it for example like this:
		 * adjustProperties( mySprite, { alpha:0.5, x:200 } );
		 */
		public function adjustProperties( obj:Object, properties:Object ):void {
			addSequenceChild( new PropertyAdjust( obj, properties ) );
		}
		
		/**
		 * will call a function on the obj Object with additional params
		 * use it for example like this:
		 * callFunction( mySprite, "addChild", myChild );
		 */
		public function callFunction( obj:Object, func:String, ...args ):void {
			var fc:FunctionCall = new FunctionCall( obj, func );
			fc.setArguments( args );
			addSequenceChild( fc );
		}
		
		/**
		 * will execute the funcCall FunctionCall and wait until the ed EventDispatcher will throw up the eventType Event.
		 * use it for example like this:
		 * awaitEvent( loader, Event.COMPLETE, new FunctionCall( loader, "load", new URLRequest( "image.jpg" ) ) );
		 */
		public function awaitEvent( ed:EventDispatcher, eventType:String, funcCall:FunctionCall ):void {
			var ea:EventAwait = new EventAwait( ed, eventType, funcCall );
			addSequenceChild( ea );
		}
		
		/**
		 * adds a break in ms to the sequence
		 */
		public function awaitBreak( ms:Number ):void {
			addSequenceChild( new Break( ms ) );
		}
		
		
		/**
		 * adds a common SequenceChild to the sequence
		 */
		public function addSequenceChild( sc:ISequenceChild ):void {
			childs.addArray( [ sc ] );
		}
		
		/**
		 * removes all childs from the sequence
		 */
		public function removeAllSequenceChilds(  ):void {
			stop();
			childs = new Iterator();
		}

		
		
		public function stop():void {
			var active:ISequenceChild = ISequenceChild( childs.current() );
			if( active != null ) {
				if( active.hasToWait() ) EventDispatcher( active ).removeEventListener( Event.COMPLETE, processNextChild );
				active.stop();
			}
		}


		public function reset():void {
			childs.reset();
		}

		/**
		 * stops looping but still finishes the current animation
		 */
		public function stopLooping():void {
			loops = false;
		}
	}
}
